fix(c4a): fix README templates and resolve "SOP tidak ditemukan" visibility logic #24
+59
-20
@@ -12,11 +12,14 @@ Dokumentasi ini dibuat untuk memudahkan pengembang atau pengguna berikutnya dala
|
|||||||
- Data SOP: `groups/C4A/data/data-sop.json`
|
- Data SOP: `groups/C4A/data/data-sop.json`
|
||||||
- Data FAQ: `groups/C4A/data/data-faq.json`
|
- Data FAQ: `groups/C4A/data/data-faq.json`
|
||||||
|
|
||||||
2. **Ikon FontAwesome (v4)**:
|
2. **Dukungan Bilingual (ID/EN)**:
|
||||||
|
Semua teks konten yang ditampilkan ke pengguna mendukung dua bahasa. Anda perlu menggunakan objek dengan properti `id` (Bahasa Indonesia) dan `en` (Bahasa Inggris) pada setiap field teks. Jika properti `en` tidak diisi atau data masih berupa *string* biasa, sistem akan otomatis menggunakan Bahasa Indonesia sebagai *fallback*.
|
||||||
|
|
||||||
|
3. **Ikon FontAwesome (v4)**:
|
||||||
Jika ingin mengubah atau menambahkan logo/ikon pada data SOP, harap gunakan daftar ikon dari **FontAwesome v4** melalui tautan berikut:
|
Jika ingin mengubah atau menambahkan logo/ikon pada data SOP, harap gunakan daftar ikon dari **FontAwesome v4** melalui tautan berikut:
|
||||||
👉 **[FontAwesome v4 Icons](https://fontawesome.com/v4/icons/)** (Contoh penggunaan: `fa-dot-circle-o`, `fa-graduation-cap`, `fa-flask`).
|
👉 **[FontAwesome v4 Icons](https://fontawesome.com/v4/icons/)** (Contoh penggunaan: `fa-dot-circle-o`, `fa-graduation-cap`, `fa-flask`).
|
||||||
|
|
||||||
3. **Field Opsional**:
|
4. **Field Opsional**:
|
||||||
Pada data SOP, properti **`staff_responsible`** (Staf Bertanggung Jawab) dan **`additional_info`** (Informasi Tambahan) bersifat **opsional**. Jika informasi tersebut tidak tersedia, kosongkan nilainya atau hapus propertinya dari JSON agar UI secara otomatis menyembunyikan bloknya.
|
Pada data SOP, properti **`staff_responsible`** (Staf Bertanggung Jawab) dan **`additional_info`** (Informasi Tambahan) bersifat **opsional**. Jika informasi tersebut tidak tersedia, kosongkan nilainya atau hapus propertinya dari JSON agar UI secara otomatis menyembunyikan bloknya.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -30,21 +33,39 @@ Silakan salin-tempel (copy-paste) format di bawah ini ke dalam array JSON yang s
|
|||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "Nama Staf Beserta Gelar",
|
"name": "Nama Staf Beserta Gelar",
|
||||||
"role": "Peran / Jabatan Staf",
|
"role": {
|
||||||
|
"id": "Peran / Jabatan Staf",
|
||||||
|
"en": "Staff Role / Title"
|
||||||
|
},
|
||||||
"image": "assets/images/nama-file-foto.png",
|
"image": "assets/images/nama-file-foto.png",
|
||||||
"location": "Lokasi Ruang Kerja / Kantor Staf",
|
"location": {
|
||||||
|
"id": "Lokasi Ruang Kerja / Kantor Staf",
|
||||||
|
"en": "Staff Workspace / Office Location"
|
||||||
|
},
|
||||||
"contact_consent": true,
|
"contact_consent": true,
|
||||||
"phone": "628xxxxxxxxxx",
|
"phone": "628xxxxxxxxxx",
|
||||||
"schedule": [
|
"schedule": [
|
||||||
{
|
{
|
||||||
"day": "Hari Kerja (contoh: Senin – Jumat)",
|
"day": {
|
||||||
"time": "Jam Kerja (contoh: 07.30–16.00 WIB)",
|
"id": "Hari Kerja (contoh: Senin – Jumat)",
|
||||||
"break": "Jam Istirahat (contoh: istirahat 12.00–13.00 WIB)"
|
"en": "Work Days (e.g., Monday – Friday)"
|
||||||
|
},
|
||||||
|
"time": "07.30–16.00 WIB",
|
||||||
|
"break": {
|
||||||
|
"id": "Jam Istirahat (contoh: istirahat 12.00–13.00 WIB)",
|
||||||
|
"en": "Break Time (e.g., break 12.00–13.00 WIB)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tasks": [
|
"tasks": [
|
||||||
"Tugas dan tanggung jawab utama 1",
|
{
|
||||||
"Tugas dan tanggung jawab utama 2"
|
"id": "Tugas dan tanggung jawab utama 1",
|
||||||
|
"en": "Main task and responsibility 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Tugas dan tanggung jawab utama 2",
|
||||||
|
"en": "Main task and responsibility 2"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -57,21 +78,33 @@ Silakan salin-tempel (copy-paste) format di bawah ini ke dalam array JSON yang s
|
|||||||
"tab_number": 9,
|
"tab_number": 9,
|
||||||
"category": "akademik",
|
"category": "akademik",
|
||||||
"icon": "fa-file-text",
|
"icon": "fa-file-text",
|
||||||
"title": "Judul Lengkap SOP",
|
"title": {
|
||||||
"description": "Deskripsi singkat mengenai SOP ini.",
|
"id": "Judul Lengkap SOP",
|
||||||
"purpose": "Tujuan dibuatnya standar operasional prosedur ini.",
|
"en": "Full SOP Title"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"id": "Deskripsi singkat mengenai SOP ini.",
|
||||||
|
"en": "Brief description of this SOP."
|
||||||
|
},
|
||||||
|
"purpose": {
|
||||||
|
"id": "Tujuan dibuatnya standar operasional prosedur ini.",
|
||||||
|
"en": "The purpose of creating this standard operating procedure."
|
||||||
|
},
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"Persyaratan administratif/akademik 1",
|
{ "id": "Persyaratan administratif/akademik 1", "en": "Administrative/academic requirement 1" },
|
||||||
"Persyaratan administratif/akademik 2"
|
{ "id": "Persyaratan administratif/akademik 2", "en": "Administrative/academic requirement 2" }
|
||||||
],
|
],
|
||||||
"procedures": [
|
"procedures": [
|
||||||
"Langkah alur prosedur ke-1",
|
{ "id": "Langkah alur prosedur ke-1", "en": "Procedure flow step 1" },
|
||||||
"Langkah alur prosedur ke-2"
|
{ "id": "Langkah alur prosedur ke-2", "en": "Procedure flow step 2" }
|
||||||
],
|
],
|
||||||
"staff_responsible": [
|
"staff_responsible": [
|
||||||
"👤 Staf Administrasi Informatika"
|
"👤 Staf Administrasi Informatika / Informatics Administrative Staff"
|
||||||
],
|
],
|
||||||
"additional_info": "Informasi tambahan penting."
|
"additional_info": {
|
||||||
|
"id": "Informasi tambahan penting.",
|
||||||
|
"en": "Important additional information."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
*Catatan: Properti `category` bernilai antara `"akademik"` atau `"praktikum"`. Properti `staff_responsible` dan `additional_info` bersifat opsional.*
|
*Catatan: Properti `category` bernilai antara `"akademik"` atau `"praktikum"`. Properti `staff_responsible` dan `additional_info` bersifat opsional.*
|
||||||
@@ -80,7 +113,13 @@ Silakan salin-tempel (copy-paste) format di bawah ini ke dalam array JSON yang s
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": 11,
|
"id": 11,
|
||||||
"question": "Pertanyaan yang sering diajukan?",
|
"question": {
|
||||||
"answer": "Jawaban dari pertanyaan tersebut. Boleh menggunakan tag HTML seperti <strong>tebal</strong>."
|
"id": "Pertanyaan yang sering diajukan?",
|
||||||
|
"en": "Frequently asked question?"
|
||||||
|
},
|
||||||
|
"answer": {
|
||||||
|
"id": "Jawaban dari pertanyaan tersebut. Boleh menggunakan tag HTML seperti <strong>tebal</strong>.",
|
||||||
|
"en": "Answer to the question. HTML tags like <strong>bold</strong> can be used."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id" class="scroll-smooth">
|
<html lang="id" class="scroll-smooth">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
@@ -1163,7 +1163,7 @@
|
|||||||
<div class="c4a-hero-tag" data-i18n="grp_c4a_hero_tag">LAYANAN AKADEMIK • INFORMATIKA UNTAN
|
<div class="c4a-hero-tag" data-i18n="grp_c4a_hero_tag">LAYANAN AKADEMIK • INFORMATIKA UNTAN
|
||||||
</div>
|
</div>
|
||||||
<h1 class="c4a-hero-title" data-i18n-html="grp_c4a_hero_title">
|
<h1 class="c4a-hero-title" data-i18n-html="grp_c4a_hero_title">
|
||||||
Staf Pendukung & <br />
|
Staf Pendukung & <br/>
|
||||||
<span class="highlight-orange">Standar Operasional Prosedur (SOP)</span>
|
<span class="highlight-orange">Standar Operasional Prosedur (SOP)</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="c4a-hero-desc" data-i18n="grp_c4a_hero_desc">Halaman ini menampilkan informasi petugas
|
<p class="c4a-hero-desc" data-i18n="grp_c4a_hero_desc">Halaman ini menampilkan informasi petugas
|
||||||
|
|||||||
+14
-6
@@ -32,7 +32,7 @@ function loc(field) {
|
|||||||
const lang = getLang();
|
const lang = getLang();
|
||||||
return field[lang] || field['id'] || '';
|
return field[lang] || field['id'] || '';
|
||||||
}
|
}
|
||||||
return field;
|
return String(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,7 +127,7 @@ export function renderStaffCards(staffs) {
|
|||||||
const scheduleHTML = schedule.map(sched =>
|
const scheduleHTML = schedule.map(sched =>
|
||||||
`<div class="schedule-item">` +
|
`<div class="schedule-item">` +
|
||||||
`<span class="s-day">${loc(sched.day)}</span>` +
|
`<span class="s-day">${loc(sched.day)}</span>` +
|
||||||
`<span class="s-time">${sched.time} <span class="s-break">| ${loc(sched.break)}</span></span>` +
|
`<span class="s-time">${loc(sched.time)} <span class="s-break">| ${loc(sched.break)}</span></span>` +
|
||||||
`</div>`
|
`</div>`
|
||||||
).join('');
|
).join('');
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ export function renderStaffCards(staffs) {
|
|||||||
<img src="${image}" alt="${name}" onerror="this.style.display='none'"/>
|
<img src="${image}" alt="${name}" onerror="this.style.display='none'"/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="staff-name">${name}</div>
|
<div class="staff-name">${loc(name)}</div>
|
||||||
<div class="staff-role">${roleText}</div>
|
<div class="staff-role">${roleText}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -283,14 +283,13 @@ function buildSopModalContent(sop) {
|
|||||||
|
|
||||||
const catLabel = getCatLabel(category);
|
const catLabel = getCatLabel(category);
|
||||||
const catClass = CATEGORY_COLORS[category] || '';
|
const catClass = CATEGORY_COLORS[category] || '';
|
||||||
|
|
||||||
const reqsHTML = requirements.map(req => `<li>${loc(req)}</li>`).join('');
|
const reqsHTML = requirements.map(req => `<li>${loc(req)}</li>`).join('');
|
||||||
const procsHTML = procedures.map(proc => `<li>${loc(proc)}</li>`).join('');
|
const procsHTML = procedures.map(proc => `<li>${loc(proc)}</li>`).join('');
|
||||||
|
|
||||||
// staffResponsible bersifat opsional — sembunyikan blok jika tidak ada
|
// staffResponsible bersifat opsional — sembunyikan blok jika tidak ada
|
||||||
const hasStaff = Array.isArray(staffResponsible) && staffResponsible.length > 0;
|
const hasStaff = Array.isArray(staffResponsible) && staffResponsible.length > 0;
|
||||||
const staffHTML = hasStaff
|
const staffHTML = hasStaff
|
||||||
? staffResponsible.map(staffName => `<span class="sop-staff-pill">${staffName}</span>`).join('')
|
? staffResponsible.map(staffName => `<span class="sop-staff-pill">${loc(staffName)}</span>`).join('')
|
||||||
: '';
|
: '';
|
||||||
const staffBlockHTML = hasStaff
|
const staffBlockHTML = hasStaff
|
||||||
? `<div class="sop-staff-block">
|
? `<div class="sop-staff-block">
|
||||||
@@ -353,15 +352,19 @@ export function renderSopCards(sops) {
|
|||||||
// simpan data global di element agar bisa diakses filter/search
|
// simpan data global di element agar bisa diakses filter/search
|
||||||
container._sopData = sops;
|
container._sopData = sops;
|
||||||
|
|
||||||
|
const emptyState = document.getElementById('sop-empty-state');
|
||||||
|
|
||||||
// Tampilkan pesan jika data SOP kosong atau gagal dimuat
|
// Tampilkan pesan jika data SOP kosong atau gagal dimuat
|
||||||
if (!sops || sops.length === 0) {
|
if (!sops || sops.length === 0) {
|
||||||
container.innerHTML = `<p class="empty-state-msg">${t('grp_c4a_empty_sop', 'Data SOP tidak tersedia saat ini.')}</p>`;
|
container.innerHTML = `<p class="empty-state-msg">${t('grp_c4a_empty_sop', 'Data SOP tidak tersedia saat ini.')}</p>`;
|
||||||
updateResultInfo(0, 0);
|
updateResultInfo(0, 0);
|
||||||
|
if (emptyState) emptyState.style.display = 'none';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.innerHTML = sops.map(buildSopCard).join('');
|
container.innerHTML = sops.map(buildSopCard).join('');
|
||||||
updateResultInfo(sops.length, sops.length);
|
updateResultInfo(sops.length, sops.length);
|
||||||
|
if (emptyState) emptyState.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update teks info jumlah hasil */
|
/** Update teks info jumlah hasil */
|
||||||
@@ -405,7 +408,12 @@ export function applySopFilter() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emptyState = document.getElementById('sop-empty-state');
|
const emptyState = document.getElementById('sop-empty-state');
|
||||||
if (emptyState) emptyState.style.display = visibleCount === 0 ? 'flex' : 'none';
|
if (emptyState) {
|
||||||
|
// Tampilkan "SOP tidak ditemukan" HANYA jika memang ada data SOP tetapi hasil filter 0.
|
||||||
|
// Jika data SOP memang kosong (tidak ada card sama sekali di data source), sembunyikan.
|
||||||
|
const hasData = container._sopData && container._sopData.length > 0;
|
||||||
|
emptyState.style.display = (hasData && visibleCount === 0) ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
updateResultInfo(visibleCount, cards.length);
|
updateResultInfo(visibleCount, cards.length);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user